package io.renren.modules.calculator.controller;

import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import java.util.*;
import org.apache.commons.math3.linear.*;

@RestController
@RequestMapping("/matrix")
public class MatrixController {

    @PostMapping("/calculate")
    public ResponseEntity<Map<String, Object>> calculateMatrix(
            @RequestBody MatrixRequest request) {

        Map<String, Object> response = new HashMap<>();

        try {
            // 验证矩阵维度
            validateMatrixDimensions(request);

            RealMatrix matrixA = createMatrix(request.getMatrixA());

            switch (request.getOperation()) {
                case "add":
                    RealMatrix matrixB = createMatrix(request.getMatrixB());
                    checkSameDimensions(matrixA, matrixB);
                    RealMatrix sum = matrixA.add(matrixB);
                    response.put("result", createResultMap(convertToArray(sum)));
                    break;

                case "subtract":
                    matrixB = createMatrix(request.getMatrixB());
                    checkSameDimensions(matrixA, matrixB);
                    RealMatrix difference = matrixA.subtract(matrixB);
                    response.put("result", createResultMap(convertToArray(difference)));
                    break;

                case "multiply":
                    matrixB = createMatrix(request.getMatrixB());
                    checkMultiplicationCompatible(matrixA, matrixB);
                    RealMatrix product = matrixA.multiply(matrixB);
                    response.put("result", createResultMap(convertToArray(product)));
                    break;

                case "transpose":
                    RealMatrix transposed = matrixA.transpose();
                    response.put("result", createResultMap(convertToArray(transposed)));
                    break;

                case "determinant":
                    checkSquareMatrix(matrixA);
                    double det = new LUDecomposition(matrixA).getDeterminant();
                    response.put("result", createSingleValueMap("value", det));
                    break;

                case "inverse":
                    checkSquareMatrix(matrixA);
                    RealMatrix inverse = new LUDecomposition(matrixA).getSolver().getInverse();
                    response.put("result", createResultMap(convertToArray(inverse)));
                    break;

                default:
                    throw new IllegalArgumentException("Unsupported operation: " + request.getOperation());
            }

            response.put("success", true);
            return ResponseEntity.ok(response);

        } catch (Exception e) {
            response.put("success", false);
            response.put("error", e.getMessage());
            return ResponseEntity.badRequest().body(response);
        }
    }

    private void validateMatrixDimensions(MatrixRequest request) {
        // 验证矩阵A的维度
        if (request.getMatrixA() != null) {
            if (request.getRowsA() > 0 && request.getColsA() > 0) {
                if (request.getMatrixA().length != request.getRowsA() ||
                        (request.getMatrixA().length > 0 &&
                                request.getMatrixA()[0].length != request.getColsA())) {
                    throw new IllegalArgumentException("Matrix A dimensions don't match specified rows/cols");
                }
            }
        }

        // 验证矩阵B的维度
        if (request.getMatrixB() != null) {
            if (request.getRowsB() > 0 && request.getColsB() > 0) {
                if (request.getMatrixB().length != request.getRowsB() ||
                        (request.getMatrixB().length > 0 &&
                                request.getMatrixB()[0].length != request.getColsB())) {
                    throw new IllegalArgumentException("Matrix B dimensions don't match specified rows/cols");
                }
            }
        }
    }

    private RealMatrix createMatrix(double[][] array) {
        return MatrixUtils.createRealMatrix(array);
    }

    private double[][] convertToArray(RealMatrix matrix) {
        return matrix.getData();
    }

    private Map<String, Object> createResultMap(double[][] matrix) {
        Map<String, Object> map = new HashMap<>();
        map.put("matrix", matrix);
        return map;
    }

    private Map<String, Object> createSingleValueMap(String key, Object value) {
        Map<String, Object> map = new HashMap<>();
        map.put(key, value);
        return map;
    }

    private void checkSameDimensions(RealMatrix a, RealMatrix b) {
        if (a.getRowDimension() != b.getRowDimension() ||
                a.getColumnDimension() != b.getColumnDimension()) {
            throw new IllegalArgumentException("Matrix dimensions must match");
        }
    }

    private void checkMultiplicationCompatible(RealMatrix a, RealMatrix b) {
        if (a.getColumnDimension() != b.getRowDimension()) {
            throw new IllegalArgumentException(
                    "Number of columns in A must match number of rows in B");
        }
    }

    private void checkSquareMatrix(RealMatrix matrix) {
        if (matrix.getRowDimension() != matrix.getColumnDimension()) {
            throw new IllegalArgumentException("Matrix must be square");
        }
    }

    public static class MatrixRequest {
        private String operation;
        private double[][] matrixA;
        private double[][] matrixB;
        private int rowsA;
        private int colsA;
        private int rowsB;
        private int colsB;

        // Getters and setters
        public String getOperation() { return operation; }
        public void setOperation(String operation) { this.operation = operation; }

        public double[][] getMatrixA() { return matrixA; }
        public void setMatrixA(double[][] matrixA) { this.matrixA = matrixA; }

        public double[][] getMatrixB() { return matrixB; }
        public void setMatrixB(double[][] matrixB) { this.matrixB = matrixB; }

        public int getRowsA() { return rowsA; }
        public void setRowsA(int rowsA) { this.rowsA = rowsA; }

        public int getColsA() { return colsA; }
        public void setColsA(int colsA) { this.colsA = colsA; }

        public int getRowsB() { return rowsB; }
        public void setRowsB(int rowsB) { this.rowsB = rowsB; }

        public int getColsB() { return colsB; }
        public void setColsB(int colsB) { this.colsB = colsB; }
    }
}